NestJS Development Standards

seedlingLast update on Jul 9, 2026
Download .md

Official Documentation

Untuk dokumentasi lengkap, kunjungi:

Project Layout

  • project
    • src
      • main.ts # Entry point
      • app.module.ts # Root module
      • modules
        • users
          • users.module.ts
          • users.controller.ts
          • users.service.ts
          • users.repository.ts
          • dto
            • create-user.dto.ts
            • update-user.dto.ts
      • common/ # Filters, interceptors, pipes, guards
      • config/ # App config
    • test
    • package.json
    • nest-cli.json

Module Pattern

// users.module.ts
@Module({
  controllers: [UsersController],
  providers: [UsersService, UsersRepository],
  exports: [UsersService],
})
export class UsersModule {}

Controller

@Controller("users")
export class UsersController {
  constructor(private readonly service: UsersService) {}

  @Get()
  async findAll() {
    return this.service.findAll();
  }

  @Post()
  async create(@Body() dto: CreateUserDto) {
    return this.service.create(dto);
  }
}

DTO Validation

Gunakan class-validator + class-transformer:

import { IsString, IsEmail, MinLength } from "class-validator";

export class CreateUserDto {
  @IsString()
  @MinLength(2)
  name: string;

  @IsEmail()
  email: string;
}

Database

Gunakan Prisma atau TypeORM:

// Prisma schema
model User {
    id    Int     @id @default(autoincrement())
    name  String
    email String  @unique
}

Testing

# Unit test
pnpm test

# E2E
pnpm test:e2e
// Controller unit test
describe("UsersController", () => {
  let controller: UsersController;
  let service: UsersService;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      controllers: [UsersController],
      providers: [{ provide: UsersService, useValue: mockService }],
    }).compile();

    controller = module.get(UsersController);
  });

  it("should return all users", async () => {
    expect(await controller.findAll()).toEqual(mockUsers);
  });
});

Configuration Management

Gunakan @nestjs/config untuk mengatur environment variables dengan validasi:

import { ConfigModule } from "@nestjs/config";
import * as Joi from "joi";

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      validationSchema: Joi.object({
        DATABASE_URL: Joi.string().required(),
        PORT: Joi.number().default(3000),
      }),
    }),
  ],
})
export class AppModule {}

Exception Filters

Gunakan global exception filter untuk menstandarkan struktur error response:

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
} from "@nestjs/common";
import { Response } from "express";

@Catch(HttpException)
export class GlobalExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const status = exception.getStatus();
    const exceptionResponse = exception.getResponse();

    response.status(status).json({
      success: false,
      error:
        typeof exceptionResponse === "string"
          ? { message: exceptionResponse }
          : exceptionResponse,
    });
  }
}

Daftarkan di main.ts: app.useGlobalFilters(new GlobalExceptionFilter()).

Interceptors

Gunakan interceptor untuk mapping response menjadi format amplop (envelope) standard:

import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";

@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, any> {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      map((data) => ({
        success: true,
        data,
      })),
    );
  }
}

Daftarkan di main.ts: app.useGlobalInterceptors(new TransformInterceptor()).

Guards (Auth & RBAC)

Gunakan Guards untuk melindungi route:

import {
  Injectable,
  CanActivate,
  ExecutionContext,
  UnauthorizedException,
} from "@nestjs/common";

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const token = request.headers.authorization;
    if (!token) throw new UnauthorizedException("Token required");
    return true;
  }
}

// Di Controller:
// @UseGuards(AuthGuard)

Deployment

Build dan run:

pnpm build
node dist/main.js

Atau dengan Docker:

FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack prepare pnpm@latest --activate && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

FROM node:22-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
CMD ["node", "dist/main.js"]